﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using Lightray.Hattrick.ChppLib;
using Lightray.Hattrick.ChppLib.EntityTypes;

namespace Lightray.Hattrick
{
    public partial class MainForm : Form
    {    
        private static Controller _controller;
        private IModel _model;

        private const string COL_FLAG_IMAGE = "COL_FLAG_IMAGE";
        private const string COL_CARDS_IMAGE = "COL_CARDS_IMAGE";
        private const string COL_CARDS_IMAGE_HEADER_TEXT = "WRN";

        private const string COL_FORWARD_SCORE_HEADER_TEXT = "[F]";
        private const string COL_WINGER_SCORE_HEADER_TEXT = "[W]";
        private const string COL_PLAYMAKER_SCORE_HEADER_TEXT = "[IM]";
        private const string COL_WINGBACK_SCORE_HEADER_TEXT = "[WB]";
        private const string COL_DEFENDER_SCORE_HEADER_TEXT = "[CD]";
        private const string COL_KEEPER_SCORE_HEADER_TEXT = "[GK]";
        private const string COL_SET_PIECES_SCORE_HEADER_TEXT = "[SP]";
        private const string COL_PENALTY_SHOT_SCORE_HEADER_TEXT = "[PT]";
        private const string COL_CAPTAIN_SCORE_HEADER_TEXT = "[CPT]";

        private Dictionary<DataGridViewColumn, DataGridViewColumn> _unboundToBoundColumnMap;

        public string CurrentlyShowingModel { get; private set; }
                          
        public MainForm()
        {
            InitializeComponent();
        }

        public MainForm(Controller c):this()
        {
            _controller = c;
        }

        public void SetModel(IModel m)
        {
            if (InvokeRequired)
            {
                Invoke(new Action<IModel>(SetModel), m);
                return;
            }

            _model = m;
            _model.DataChanged += new EventHandler(model_DataChanged);
            blankGrid();

            if (_model.Data != null)
            {
                gridPlayers.DataSource = _model.Data;
                updateGrid();
            }        
        }

        public void UpdateFailed()
        {
            if (InvokeRequired)
            {
                Invoke(new Action(UpdateFailed));
                return;
            }

            MessageBox.Show("Download failed... Hattrick may be down, try again later", "CHPP Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            setUiUpdateMode(false);
        }

        public void UpdateFinished()
        {
            if (InvokeRequired)
            {
                Invoke(new Action(UpdateFinished));
                return;
            }

            setUiUpdateMode(false);
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            setGridDoubleBuffered();
            addCustomUnboundColumns();
            initCurrencyCombo();
            initModelCombo();
        }

        private void updateGrid()
        {
            gridPlayers.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None;
            pairBoundToUnboundColumns();
            styleColumns();
            styleRows();
            gridPlayers.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells;
            resizeFormToGrid();            
        }

        private void styleRows()
        {
            foreach (DataGridViewRow row in gridPlayers.Rows)
            {
                var player = _model.PlayerFromRow(row.DataBoundItem as DataRowView);

                highlightInjuredPlayerRow(row);

                if (_model.HasProgressReport)
                    showPlayerProgress(row);

                if (player is YouthPlayer)
                    showUnlockedSkills(row);
            }
        }

        private void pairBoundToUnboundColumns()
        {
            if (_unboundToBoundColumnMap == null)
                _unboundToBoundColumnMap = new Dictionary<DataGridViewColumn, DataGridViewColumn>();

            /* image columns (which are unbound) are based on hidden bound columns from the model
             * this will hold a refrence to the bound columns for sorting purpoues
             * thus avoiding the need to hold the column name as a const in the view too */
            foreach (DataGridViewColumn col in gridPlayers.Columns)
            {
                switch (_model.GetColumnDescription(col.DataPropertyName))
                {
                    case ModelColumnDescription.Nationality:
                        _unboundToBoundColumnMap[gridPlayers.Columns[COL_FLAG_IMAGE]] = col;
                        break;
                    case ModelColumnDescription.Cards :
                        _unboundToBoundColumnMap[gridPlayers.Columns[COL_CARDS_IMAGE]] = col;
                        break;
                }
            }            
        }

        private void showPlayerProgress(DataGridViewRow row)
        {
            var pairs = _model.PlayerProgressFromRow(row.DataBoundItem as DataRowView);
            if (pairs == null)
                return;

            foreach (var pair in pairs)
                styleCellPlayerProgress(row.Cells[pair.Key], pair.Value);
        }

        private void showUnlockedSkills(DataGridViewRow row)
        {
            var unlockedSkillColumns = _model.UnlockedSkillsFromRow(row.DataBoundItem as DataRowView);
            if (unlockedSkillColumns == null)
                return;

            foreach (var columnName in unlockedSkillColumns)
                styleCellUnlockedSkill(row.Cells[columnName]);
        }

        private void addCustomUnboundColumns()
        {
            gridPlayers.Columns.Add(createImageColumn(COL_FLAG_IMAGE, ""));
            gridPlayers.Columns.Add(createImageColumn(COL_CARDS_IMAGE, COL_CARDS_IMAGE_HEADER_TEXT));
        }

        private DataGridViewImageColumn createImageColumn(string colName, string headerText)
        {
            DataGridViewImageColumn col = new DataGridViewImageColumn();
            col.Name = colName;
            col.HeaderText = headerText;
            col.DefaultCellStyle.NullValue = null;
            col.Visible = false;
            return col;
        }

        private void styleColumns()
        {
            foreach (DataGridViewColumn col in gridPlayers.Columns)
            {
                col.SortMode = DataGridViewColumnSortMode.NotSortable;

                switch (_model.GetColumnDescription(col.DataPropertyName))
                {
                    case ModelColumnDescription.Name:
                        col.Width = 75;
                        break;

                    case ModelColumnDescription.Age:
                        col.Width = 35;
                        break;

                    case ModelColumnDescription.Skill:
                        col.DefaultCellStyle.Format = "0";
                        col.Width = 38;
                        break;

                    case ModelColumnDescription.SkillScore:
                        if (_model.GetRoleColumnName(col.DataPropertyName) == PlayerRole.Captain)
                            col.DefaultCellStyle.Format ="0";
                        else
                            col.DefaultCellStyle.Format = ".00";
                        col.Width = 43;
                        col.HeaderText = renameScoreColumn(col);
                        break;

                    case ModelColumnDescription.SkillPotential:
                        col.Visible = false;
                        break;

                    case ModelColumnDescription.TSI:
                        col.Width = 55;
                        break;

                    case ModelColumnDescription.Speciality:
                        col.Width = 85;
                        break;

                    case ModelColumnDescription.Injury:
                        col.Width = 30;
                        break;

                    case ModelColumnDescription.TimeUntilPromotion:
                        col.Width = 70;
                        break;

                    case ModelColumnDescription.Nationality:
                    case ModelColumnDescription.PlayerInstance:
                    case ModelColumnDescription.Cards:
                        col.Visible = false;
                        break;

                    default: // unbound cells
                        const int FIRST_COLUMN_INDEX = 0;
                        const int SEVENTH_COLUMN_INDEX = FIRST_COLUMN_INDEX + 6;

                        switch (col.Name)
                        {
                            case COL_CARDS_IMAGE:
                                col.DefaultCellStyle.NullValue = null;
                                col.Width = 38;
                                col.Visible = true;

                                col.DisplayIndex = SEVENTH_COLUMN_INDEX;
                                break;

                            case COL_FLAG_IMAGE:
                                col.Width = 19;
                                col.Visible = _model is SeniorTeamModel;
                                col.DisplayIndex = FIRST_COLUMN_INDEX;
                                break;
                        }
                        break;
                }
            }
        }

        private string renameScoreColumn(DataGridViewColumn col)
        {
            switch (_model.GetRoleColumnName(col.DataPropertyName))
            {
                case PlayerRole.Forward:
                    return COL_FORWARD_SCORE_HEADER_TEXT;
                case PlayerRole.Winger:
                    return COL_WINGER_SCORE_HEADER_TEXT;
                case PlayerRole.Playmaking:
                    return COL_PLAYMAKER_SCORE_HEADER_TEXT;
                case PlayerRole.Wingback:
                    return COL_WINGBACK_SCORE_HEADER_TEXT;
                case PlayerRole.Defender:
                    return COL_DEFENDER_SCORE_HEADER_TEXT;
                case PlayerRole.Goalkeeper:
                    return COL_KEEPER_SCORE_HEADER_TEXT;
                case PlayerRole.SetPieces:
                    return COL_SET_PIECES_SCORE_HEADER_TEXT;
                case PlayerRole.PenaltyTaker:
                    return COL_PENALTY_SHOT_SCORE_HEADER_TEXT;
                case PlayerRole.Captain:
                    return COL_CAPTAIN_SCORE_HEADER_TEXT;
            }

            return "";
        }      

        private void setGridDoubleBuffered()
        {
            typeof(DataGridView).InvokeMember("DoubleBuffered",
                                              BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
                                              null,
                                              gridPlayers,
                                              new object[] { true });
        }

        private void resizeFormToGrid()
        {
            if (gridPlayers.Rows.Count > 0)
                this.Size = gridPlayers.PreferredSize + new Size(-10, 36);

            if (this.Height > Screen.FromControl(gridPlayers).Bounds.Height)
                this.Height = Screen.FromControl(gridPlayers).Bounds.Height - 200;
        }      

        private void gridPlayers_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
        {
            var cell = gridPlayers[e.ColumnIndex, e.RowIndex];
            var player = _model.PlayerFromRow(cell.OwningRow.DataBoundItem as DataRowView);

            formatCellValue(e, cell, player);           
            setCellToolTip(cell, player);
        }        

        private void styleCellPlayerProgress(DataGridViewCell cell, ProgressCharacteristic characteristic)
        {
            switch (characteristic)
            {
                case ProgressCharacteristic.Positive:
                    cell.Style.BackColor = Color.LightGreen;
                    break;
                case ProgressCharacteristic.Negative:
                    cell.Style.BackColor = Color.Salmon;
                    break;
            }
        }

        private void styleCellUnlockedSkill(DataGridViewCell cell)
        {
            cell.Style.BackColor = Color.Yellow;
        }

        private void formatCellValue(DataGridViewCellFormattingEventArgs e, DataGridViewCell cell, BasicPlayer player)
        {
            object formattedValue;
            if (formatCellValue(cell, player, out formattedValue))
                e.Value = formattedValue;
        }

        private bool formatCellValue(DataGridViewCell cell, BasicPlayer player, out object newValue)
        {
            switch (_model.GetColumnDescription(cell.OwningColumn.DataPropertyName))
            {
                case ModelColumnDescription.Age:
                    newValue = truncWithoutRounding(player.Age);
                    return true;

                case ModelColumnDescription.Salary:
                    float updatedSalary = (float)(player as Player).Salary / Currency.GetRate(_controller.PreferredCurrency);
                    newValue = updatedSalary.ToString("N0");
                    return true;

                case ModelColumnDescription.Injury:
                    newValue = injuryValueToString(player.InjuryLevel);
                    return true;

                case ModelColumnDescription.TimeUntilPromotion:
                    newValue = formatTimeLeftToPromotion(player as YouthPlayer);
                    return true;
            }

            newValue = null;
            return false;
        }

        private string formatTimeLeftToPromotion(YouthPlayer player)
        {
            if (player.CanBePromoted)
                return "Now!";
            return player.CanBePromotedTime.Days + " Days";
        }

        private void setCellToolTip(DataGridViewCell cell, BasicPlayer player)
        {
            switch (_model.GetColumnDescription(cell.OwningColumn.DataPropertyName))
            {
                case ModelColumnDescription.Skill:
                    if (player is Player)
                        cell.ToolTipText = ((SkillLevel)cell.Value).ToString();
                    else
                        cell.ToolTipText = ((YouthPlayerSkill)cell.Value).TextualDescription();
                    break;
                case ModelColumnDescription.Age:
                    cell.ToolTipText = ((HattrickAge)cell.Value).ToString();
                    break;
                case ModelColumnDescription.Name:                                        
                    cell.ToolTipText = player.ID.ToString();
                    break;
                default: // unbound cells
                    switch (cell.OwningColumn.Name)
                    {
                        case COL_FLAG_IMAGE:
                            if (player is Player)
                                cell.ToolTipText = (player as Player).Birthplace.ToString();
                            break;
                    }
                    break;
            }
        }      

        private void gridPlayers_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
        {
            sortColumn(gridPlayers.Columns[e.ColumnIndex]);         
        }

        private void sortColumn(DataGridViewColumn col)
        {
            string columnName = 
                columnIsBound(col) ? col.DataPropertyName : _unboundToBoundColumnMap[col].DataPropertyName;

            _model.Sort(columnName);
        }

        private bool columnIsBound(DataGridViewColumn col)
        {
            return col.DataPropertyName != "";
        }
        
        private void gridPlayers_UnboundCellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
        {
            var col = gridPlayers.Columns[e.ColumnIndex];
            var row = gridPlayers.Rows[e.RowIndex];
            var player = _model.PlayerFromRow(row.DataBoundItem as DataRowView);

            switch (col.Name)
            {
                case COL_CARDS_IMAGE:
                    e.Value = ResourceHandler.ImageForCards(player.Cards);
                    break;
                case COL_FLAG_IMAGE:
                    if (player is Player)
                        e.Value = ResourceHandler.CountryFlag((player as Player).Birthplace);
                    break;
            }
        }
  
        private double truncWithoutRounding(HattrickAge age)
        {
            return Math.Truncate(age.YearsAsFraction() * 10) / 10;
        }

        private void highlightInjuredPlayerRow(DataGridViewRow row)
        {
                int injuryLevel = _model.PlayerFromRow(row.DataBoundItem as DataRowView).InjuryLevel;
                if (injuryLevel >= 0)
                    row.DefaultCellStyle.ForeColor = Color.Red;
                else
                    row.DefaultCellStyle.ForeColor = Color.Black;    
        }

        private string injuryValueToString(int injuryValue)
        {
            switch (injuryValue)
            {
                case -1:
                    return "";
                case 0:
                    return "+";
                default:
                    return "+" + injuryValue;
            }
        }

        private void btnUpdateData_Click(object sender, EventArgs e)
        {
            setUiUpdateMode(true);
            _controller.StartUpdateData();
        }
    
        private void setUiUpdateMode(bool startOfUpdate)
        {
            imgAjax.Visible = startOfUpdate;
            btnUpdateData.Enabled = !startOfUpdate;
            gridPlayers.Enabled = !startOfUpdate;
        }

        private void model_DataChanged(object sender, EventArgs e)
        {
            updateGrid();
        }

        private void initCurrencyCombo()
        {
            object[] countries = Enum.GetValues(typeof(Country)).Cast<object>().OrderBy(countryValue => countryValue.ToString()).ToArray();
            cboCurrency.Items.AddRange(countries);
            cboCurrency.SelectedItem = _controller.PreferredCurrency;
        }

        private void initModelCombo()
        {
            cboTeamChoice.Items.AddRange(_controller.AvailableModels);
            setSeniorTeamAsStartupDefault();
        }

        private void setSeniorTeamAsStartupDefault()
        {
            cboTeamChoice.SelectedIndex = 0;
        }
 
        private void cboCurrency_SelectedIndexChanged(object sender, EventArgs e)
        {
            _controller.PreferredCurrency = (Country)cboCurrency.SelectedItem;
            gridPlayers.Refresh();
        }

        private void cboTeamChoice_SelectedIndexChanged(object sender, EventArgs e)
        {
            string modelName = cboTeamChoice.SelectedItem as string;
            _controller.OnViewChangeModel(modelName);
            CurrentlyShowingModel = modelName;
        }

        private void blankGrid()
        {
            gridPlayers.DataSource = null;

            // hide custom columns
            foreach (DataGridViewColumn col in gridPlayers.Columns)
                col.Visible = false;
        }
    }
}
